package com.hilihili.socketio.handler;

import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.hilihili.domain.dto.AudienceDTO;
import com.hilihili.socketio.cache.ClientCache;
import com.hilihili.util.DataFormatUtil;
import com.hilihili.constant.RedisVideoConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Set;

/**
 * socketio 处理业务逻辑的 Handler
 *
 * @author machi
 * @date 2023/12/05
 */
@Component
public class SocketIOHandler {
    @Resource
    private ClientCache clientCache;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    /**
     *undefined
     */
    public static final String UNDEFINDED = "undefined";

    /**
     * 客户端连接的时候触发，前端js触发：socket = io.connect("http://localhost:9092");
     *
     * @param client SocketIOClient
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        String sessionId = null;
        if (UNDEFINDED.equals(userId)){
            //匿名用户观看视频
            sessionId = client.getSessionId().toString();
        }else {
            sessionId = userId;
        }
        //存入缓存，方便后续使用
        clientCache.saveClient(userId, client.getSessionId(), client);
        System.out.println("userId: " + sessionId + "连接建立成功 - " + sessionId);
    }


    /**
     * 客户端关闭连接时触发：前端js触发：socket.disconnect();
     *
     * @param client SocketIOClient
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        String sessionId = null;
        if (UNDEFINDED.equals(userId)){
            //匿名用户观看视频
            sessionId = client.getSessionId().toString();
        }else {
            sessionId = userId;
        }
        clientCache.deleteSessionClientByUserId(userId, client.getSessionId());
        System.out.println("userId: " + sessionId + "连接关闭成功 - " + sessionId);
    }

    /**
     * 自定义消息事件，客户端js触发:socket.emit('messageevent', {msgContent: msg});时触发该方法
     * 前端js的 socket.emit("事件名","参数数据")方法，是触发后端自定义消息事件的时候使用的
     * 前端js的 socket.on("事件名",匿名函数(服务器向客户端发送的数据))为监听服务器端的事件
     */
    @OnEvent("joinRoom")
    public void joinRoom(SocketIOClient client, AckRequest ackRequest, AudienceDTO audienceDTO) {
        Long userId = audienceDTO.getUserId();
        String sessionId = null;
        if (userId == null){
            //匿名用户观看视频
            sessionId = client.getSessionId().toString();
        }else {
            sessionId = userId.toString();
        }
        String videoId = audienceDTO.getVideoId();
        HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();
        ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
        // opsForHash.increment（key,filed,incr） incr:步长
        //进入房间在线人数加一，没有则创建
        //此过程防止 用户多次打开同一页面 在线观看人数多次计数
        opsForHash.increment(RedisVideoConstant.VIEWS_NUMBER + videoId, sessionId, 1L);
        //添加客户端连接 防止服务端宕机key一直存在redis当中
        opsForZSet.add(RedisVideoConstant.VIEWS_NUMBER_LIVE + videoId, sessionId, System.currentTimeMillis() + 1000L * 60L);
    }

    @OnEvent("leaveRoom")
    public void leaveRoom(SocketIOClient client, AckRequest ackRequest, AudienceDTO audienceDTO) {
        Long userId = audienceDTO.getUserId();
        String sessionId = null;
        if (userId == null){
            //匿名用户观看视频
            sessionId = client.getSessionId().toString();
        }else {
            sessionId = userId.toString();
        }
        String videoId = audienceDTO.getVideoId();
        ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
        HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();
        Object count = opsForHash.get(RedisVideoConstant.VIEWS_NUMBER + videoId, sessionId);
        // opsForHash.increment（key,filed,incr） incr:步长
        //进入房间在线人数加一，没有则创建
        //此过程防止 用户多次打开同一页面 在线观看人数多次计数
        if (count == null) {
            return;
        }
        //客户端断开连接
        //Integer.parseInt(count.toString()) <= 1 只进入一次直接删除redis key
        if (Integer.parseInt(count.toString()) <= 1) {
            opsForHash.delete(RedisVideoConstant.VIEWS_NUMBER + videoId, sessionId);
            opsForZSet.remove(RedisVideoConstant.VIEWS_NUMBER_LIVE + videoId, sessionId);
        } else {
            //Integer.parseInt(count.toString()) > 1 进入多次
            opsForHash.increment(RedisVideoConstant.VIEWS_NUMBER + videoId, sessionId, -1L);
        }
    }

    /**
     * 定时推送视频在线观看人数
     */
    @OnEvent("getViewsNumber")
    public void getViewsNumber(SocketIOClient client, AckRequest ackRequest, AudienceDTO audienceDTO) {
        String videoId = audienceDTO.getVideoId();
        Long userId = audienceDTO.getUserId();
        String sessionId = null;
        if (userId == null){
            //匿名用户观看视频
            sessionId = client.getSessionId().toString();
        }else {
            sessionId = userId.toString();
        }
        HashOperations<String, Object, Object> opsForHash = stringRedisTemplate.opsForHash();
        ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();
        //获取在线观看人数
        Long viewsNumber = opsForHash.size(RedisVideoConstant.VIEWS_NUMBER + videoId);
        //获取连接过期 客户端
        Set<String> death = opsForZSet.rangeByScore(RedisVideoConstant.VIEWS_NUMBER_LIVE + videoId, 0, System.currentTimeMillis());
        //移除过期连接
        if (death != null && !death.isEmpty()) {
            opsForZSet.remove(RedisVideoConstant.VIEWS_NUMBER_LIVE + videoId, death.toArray());
            opsForHash.delete(RedisVideoConstant.VIEWS_NUMBER + videoId, death.toArray());
        }
        //增加客户端连接存活时间
        opsForZSet.add(RedisVideoConstant.VIEWS_NUMBER_LIVE + videoId, sessionId, System.currentTimeMillis() + 1000L * 60L);
        client.sendEvent("getViewsNumber", DataFormatUtil.numChange(viewsNumber));
    }
}


